package api

import (
	"context"
	"gitee.com/saxon134/go-utils/saData"
	"gitee.com/saxon134/go-utils/saData/saHit"
	"gitee.com/saxon134/go-utils/saLog"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

/************************************/
/********** Context Model ********/
/************************************/

type Context struct {
	context.Context

	// 请求ID
	Rid string `json:"rid,omitempty"`

	// header获取的数据
	Headers

	// query中获取的分页、排序数据
	Paging Paging
	Sort   Sort

	// 请求原始参数会保存在这里，bind只是获取想要的数据，可能会丢失一部分数据
	// 其他服务调用context方法需要设置参数也可以通过对其赋值进行传参
	// Bind的时候，优先使用该参数，无数据才会从请求中获取
	RawParam interface{} `json:"rawParam,omitempty"`
}

type Headers struct {
	Account AccountJwt `json:"account,omitempty"`
}

type Paging struct {
	Limit  int   `json:"limit,omitempty"` //默认值为10，即便Valid为false，Limit也不会空
	Offset int   `json:"offset,omitempty"`
	Total  int64 `json:"total,omitempty"` //默认为0，前端如果传了后端就不需要再count了
	Valid  bool  `json:"valid,omitempty"` //有些场景，不传分页参数，表示需要获取所有数据。具体业务代码控制
}

type Sort struct {
	Key  string `json:"key,omitempty"`
	Desc bool   `json:"desc,omitempty"`
}

type AccountJwt struct {
	Id int64 `json:"id,omitempty"`
}

type Response struct {
	Result interface{} `json:"result"`
	Ext    interface{} `json:"ext,omitempty"`
	Total  int64       `json:"total,omitempty"` //分页时的总数
}

// Count 计算总数并判断是否没有更多数据了
func (m *Paging) Count(orm *gorm.DB) (isAll bool) {
	if m.Total <= 0 {
		orm.Count(&m.Total)
	}
	return m.Total <= int64(m.Offset)
}

/************************************/
/********** Context Handler ********/
/************************************/

// Bind 绑定请求参数
// objPtr只能是struct指针，或者map[string]interface{}
func (ctx *Context) Bind(objPtr interface{}) {
	var err error
	if ctx.RawParam != nil {
		str, _ := saData.ToStr(ctx.RawParam)
		if str != "" {
			err = saData.StrToModel(str, objPtr)
			if err == nil {
				return
			}
		}
	}

	if c, ok := ctx.Context.(*gin.Context); ok {
		if c.Request.Method == "GET" {
			if params, ok := objPtr.(map[string]interface{}); ok && params != nil {
				var values = c.Request.URL.Query()
				for k, v := range values {
					params[k] = v[0]
				}
			} else {
				err = c.ShouldBindQuery(objPtr)
				if err != nil {
					saLog.Err("api bind params error:", err)
				}
			}
		} else if c.Request.Method == "POST" {
			tp := c.Request.Header.Get("Content-Type")
			tp = saHit.Str(tp != "", tp, c.Request.Header.Get("content-type"))

			if tp == "application/json" {
				// 备份rawData 目的是为了能够多次bind，并且http.ResErr时打印传参
				rawData, _ := c.GetRawData()
				if len(rawData) > 0 {
					params, ok := objPtr.(map[string]interface{})
					if ok && params != nil {
						err = saData.BytesToModel(rawData, &params)
					} else {
						err = saData.BytesToModel(rawData, objPtr)
					}
					if err != nil {
						saLog.Err("api bind params error:", err)
					}
					ctx.RawParam = rawData
				}
			} else {
				err = c.ShouldBind(objPtr)
				if err != nil {
					saLog.Err("api bind params error:", err)
				}
				ctx.RawParam = c.Request.URL.RawQuery
			}
		} else {
			saLog.Err("GET/POST之外不支持")
		}

		// 暂不开启
		// err = saData.ValidCheck(objPtr)

	} else {
		str := saData.String(ctx.RawParam)
		params := objPtr.(map[string]interface{})
		if params != nil {
			err = saData.StrToModel(str, &params)
		} else {
			err = saData.StrToModel(str, objPtr)
		}
		err = saData.StrToModel(str, objPtr)
		if err != nil {
			saLog.Err("api bind params error:", err)
		}
	}
}
